Skip to main content

第 11 章: 監控設定

CloudWatch

aws_cloudwatch_metric_alarm
resource "aws_cloudwatch_metric_alarm" "foobar" {
alarm_name = "terraform-test-foobar5"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 2
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = 120
statistic = "Average"
threshold = 80
alarm_description = "This metric monitors ec2 cpu utilization"
insufficient_data_actions = []
}
Conjunction with Scaling Policies
resource "aws_autoscaling_policy" "bat" {
name = "foobar3-terraform-test"
scaling_adjustment = 4
adjustment_type = "ChangeInCapacity"
cooldown = 300
autoscaling_group_name = aws_autoscaling_group.bar.name
}

resource "aws_cloudwatch_metric_alarm" "bat" {
alarm_name = "terraform-test-foobar5"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 2
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = 120
statistic = "Average"
threshold = 80

dimensions = {
AutoScalingGroupName = aws_autoscaling_group.bar.name
}

alarm_description = "This metric monitors ec2 cpu utilization"
alarm_actions = [aws_autoscaling_policy.bat.arn]
}
with an Expression
resource "aws_cloudwatch_metric_alarm" "foobar" {
alarm_name = "terraform-test-foobar"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 2
threshold = 10
alarm_description = "Request error rate has exceeded 10%"
insufficient_data_actions = []

metric_query {
id = "e1"
expression = "m2/m1*100"
label = "Error Rate"
return_data = "true"
}

metric_query {
id = "m1"

metric {
metric_name = "RequestCount"
namespace = "AWS/ApplicationELB"
period = 120
stat = "Sum"
unit = "Count"

dimensions = {
LoadBalancer = "app/web"
}
}
}

metric_query {
id = "m2"

metric {
metric_name = "HTTPCode_ELB_5XX_Count"
namespace = "AWS/ApplicationELB"
period = 120
stat = "Sum"
unit = "Count"

dimensions = {
LoadBalancer = "app/web"
}
}
}
}
resource "aws_cloudwatch_metric_alarm" "xx_anomaly_detection" {
alarm_name = "terraform-test-foobar"
comparison_operator = "GreaterThanUpperThreshold"
evaluation_periods = 2
threshold_metric_id = "e1"
alarm_description = "This metric monitors ec2 cpu utilization"
insufficient_data_actions = []

metric_query {
id = "e1"
expression = "ANOMALY_DETECTION_BAND(m1)"
label = "CPUUtilization (Expected)"
return_data = "true"
}

metric_query {
id = "m1"
return_data = "true"
metric {
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = 120
stat = "Average"
unit = "Count"

dimensions = {
InstanceId = "i-abc123"
}
}
}
}
monitoring Healthy Hosts on NLB using Target Group and NLB
resource "aws_cloudwatch_metric_alarm" "nlb_healthyhosts" {
alarm_name = "alarmname"
comparison_operator = "LessThanThreshold"
evaluation_periods = 1
metric_name = "HealthyHostCount"
namespace = "AWS/NetworkELB"
period = 60
statistic = "Average"
threshold = var.logstash_servers_count
alarm_description = "Number of healthy nodes in Target Group"
actions_enabled = "true"
alarm_actions = [aws_sns_topic.sns.arn]
ok_actions = [aws_sns_topic.sns.arn]
dimensions = {
TargetGroup = aws_lb_target_group.lb-tg.arn_suffix
LoadBalancer = aws_lb.lb.arn_suffix
}
}
Cloudwatch metric stream
resource "aws_cloudwatch_metric_stream" "main" {
name = "my-metric-stream"
role_arn = aws_iam_role.metric_stream_to_firehose.arn
firehose_arn = aws_kinesis_firehose_delivery_stream.s3_stream.arn
output_format = "json"

include_filter {
namespace = "AWS/EC2"
metric_names = ["CPUUtilization", "NetworkOut"]
}

include_filter {
namespace = "AWS/EBS"
metric_names = []
}
}

# https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-metric-streams-trustpolicy.html
data "aws_iam_policy_document" "streams_assume_role" {
statement {
effect = "Allow"

principals {
type = "Service"
identifiers = ["streams.metrics.cloudwatch.amazonaws.com"]
}

actions = ["sts:AssumeRole"]
}
}

resource "aws_iam_role" "metric_stream_to_firehose" {
name = "metric_stream_to_firehose_role"
assume_role_policy = data.aws_iam_policy_document.streams_assume_role.json
}

# https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-metric-streams-trustpolicy.html
data "aws_iam_policy_document" "metric_stream_to_firehose" {
statement {
effect = "Allow"

actions = [
"firehose:PutRecord",
"firehose:PutRecordBatch",
]

resources = [aws_kinesis_firehose_delivery_stream.s3_stream.arn]
}
}
resource "aws_iam_role_policy" "metric_stream_to_firehose" {
name = "default"
role = aws_iam_role.metric_stream_to_firehose.id
policy = data.aws_iam_policy_document.metric_stream_to_firehose.json
}

resource "aws_s3_bucket" "bucket" {
bucket = "metric-stream-test-bucket"
}

resource "aws_s3_bucket_acl" "bucket_acl" {
bucket = aws_s3_bucket.bucket.id
acl = "private"
}

data "aws_iam_policy_document" "firehose_assume_role" {
statement {
effect = "Allow"

principals {
type = "Service"
identifiers = ["firehose.amazonaws.com"]
}

actions = ["sts:AssumeRole"]
}
}

resource "aws_iam_role" "firehose_to_s3" {
assume_role_policy = data.aws_iam_policy_document.firehose_assume_role.json
}

data "aws_iam_policy_document" "firehose_to_s3" {
statement {
effect = "Allow"

actions = [
"s3:AbortMultipartUpload",
"s3:GetBucketLocation",
"s3:GetObject",
"s3:ListBucket",
"s3:ListBucketMultipartUploads",
"s3:PutObject",
]

resources = [
aws_s3_bucket.bucket.arn,
"${aws_s3_bucket.bucket.arn}/*",
]
}
}

resource "aws_iam_role_policy" "firehose_to_s3" {
name = "default"
role = aws_iam_role.firehose_to_s3.id
policy = data.aws_iam_policy_document.firehose_to_s3.json
}

resource "aws_kinesis_firehose_delivery_stream" "s3_stream" {
name = "metric-stream-test-stream"
destination = "extended_s3"

extended_s3_configuration {
role_arn = aws_iam_role.firehose_to_s3.arn
bucket_arn = aws_s3_bucket.bucket.arn
}
}
Cloudwatch Dashboard
resource "aws_cloudwatch_dashboard" "main" {
dashboard_name = "my-dashboard"

dashboard_body = jsonencode({
widgets = [
{
type = "metric"
x = 0
y = 0
width = 12
height = 6

properties = {
metrics = [
[
"AWS/EC2",
"CPUUtilization",
"InstanceId",
"i-012345"
]
]
period = 300
stat = "Average"
region = "us-east-1"
title = "EC2 Instance CPU"
}
},
{
type = "text"
x = 0
y = 7
width = 3
height = 3

properties = {
markdown = "Hello world"
}
}
]
})
}
Cloudwatch Composite Alarm
resource "aws_cloudwatch_composite_alarm" "example" {
alarm_description = "This is a composite alarm!"
alarm_name = "example-composite-alarm"

alarm_actions = aws_sns_topic.example.arn
ok_actions = aws_sns_topic.example.arn

alarm_rule = <<EOF
ALARM(${aws_cloudwatch_metric_alarm.alpha.alarm_name}) OR
ALARM(${aws_cloudwatch_metric_alarm.bravo.alarm_name})
EOF

actions_suppressor {
alarm = "suppressor-alarm"
extension_period = 10
wait_period = 20
}
}

CloudWatch log

Cloudwatch Log Stream
resource "aws_cloudwatch_log_group" "yada" {
name = "Yada"
}

resource "aws_cloudwatch_log_stream" "foo" {
name = "SampleLogStream1234"
log_group_name = aws_cloudwatch_log_group.yada.name
}
Cloudwatch Log Metric Filter
resource "aws_cloudwatch_log_metric_filter" "yada" {
name = "MyAppAccessCount"
pattern = ""
log_group_name = aws_cloudwatch_log_group.dada.name

metric_transformation {
name = "EventCount"
namespace = "YourNamespace"
value = "1"
}
}

resource "aws_cloudwatch_log_group" "dada" {
name = "MyApp/access.log"
}
CloudWatch Log destionation
resource "aws_cloudwatch_log_destination" "test_destination" {
name = "test_destination"
role_arn = aws_iam_role.iam_for_cloudwatch.arn
target_arn = aws_kinesis_stream.kinesis_for_cloudwatch.arn
}
CLoudWatch log destination Policy
resource "aws_cloudwatch_log_destination" "test_destination" {
name = "test_destination"
role_arn = aws_iam_role.iam_for_cloudwatch.arn
target_arn = aws_kinesis_stream.kinesis_for_cloudwatch.arn
}

data "aws_iam_policy_document" "test_destination_policy" {
statement {
effect = "Allow"

principals {
type = "AWS"

identifiers = [
"123456789012",
]
}

actions = [
"logs:PutSubscriptionFilter",
]

resources = [
aws_cloudwatch_log_destination.test_destination.arn,
]
}
}

resource "aws_cloudwatch_log_destination_policy" "test_destination_policy" {
destination_name = aws_cloudwatch_log_destination.test_destination.name
access_policy = data.aws_iam_policy_document.test_destination_policy.json
}
CloudWatch Log Group
resource "aws_cloudwatch_log_group" "yada" {
name = "Yada"

tags = {
Environment = "production"
Application = "serviceA"
}
}

CloudTrail

CloudTrail
resource "aws_cloudtrail" "example" {
depends_on = [aws_s3_bucket_policy.example]

name = "example"
s3_bucket_name = aws_s3_bucket.example.id
s3_key_prefix = "prefix"
include_global_service_events = false
}

resource "aws_s3_bucket" "example" {
bucket = "tf-test-trail"
force_destroy = true
}

data "aws_iam_policy_document" "example" {
statement {
sid = "AWSCloudTrailAclCheck"
effect = "Allow"

principals {
type = "Service"
identifiers = ["cloudtrail.amazonaws.com"]
}

actions = ["s3:GetBucketAcl"]
resources = [aws_s3_bucket.example.arn]
condition {
test = "StringEquals"
variable = "aws:SourceArn"
values = ["arn:${data.aws_partition.current.partition}:cloudtrail:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:trail/example"]
}
}

statement {
sid = "AWSCloudTrailWrite"
effect = "Allow"

principals {
type = "Service"
identifiers = ["cloudtrail.amazonaws.com"]
}

actions = ["s3:PutObject"]
resources = ["${aws_s3_bucket.example.arn}/prefix/AWSLogs/${data.aws_caller_identity.current.account_id}/*"]

condition {
test = "StringEquals"
variable = "s3:x-amz-acl"
values = ["bucket-owner-full-control"]
}
condition {
test = "StringEquals"
variable = "aws:SourceArn"
values = ["arn:${data.aws_partition.current.partition}:cloudtrail:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:trail/example"]
}
}
}

resource "aws_s3_bucket_policy" "example" {
bucket = aws_s3_bucket.example.id
policy = data.aws_iam_policy_document.example.json
}

data "aws_caller_identity" "current" {}

data "aws_partition" "current" {}

data "aws_region" "current" {}
Logging All Lambda Function Invocations By Using Basic Event Selectors
resource "aws_cloudtrail" "example" {
# ... other configuration ...

event_selector {
read_write_type = "All"
include_management_events = true

data_resource {
type = "AWS::Lambda::Function"
values = ["arn:aws:lambda"]
}
}
}
Logging All S3 Object Events By Using Basic Event Selectors
resource "aws_cloudtrail" "example" {
# ... other configuration ...

event_selector {
read_write_type = "All"
include_management_events = true

data_resource {
type = "AWS::S3::Object"
values = ["arn:aws:s3"]
}
}
}
Logging Individual S3 Bucket Events By Using Basic Event Selectors
data "aws_s3_bucket" "important-bucket" {
bucket = "important-bucket"
}

resource "aws_cloudtrail" "example" {
# ... other configuration ...

event_selector {
read_write_type = "All"
include_management_events = true

data_resource {
type = "AWS::S3::Object"

# Make sure to append a trailing '/' to your ARN if you want
# to monitor all objects in a bucket.
values = ["${data.aws_s3_bucket.important-bucket.arn}/"]
}
}
}
Logging All S3 Object Events Except For Two S3 Buckets By Using Advanced Event Selectors
data "aws_s3_bucket" "not-important-bucket-1" {
bucket = "not-important-bucket-1"
}

data "aws_s3_bucket" "not-important-bucket-2" {
bucket = "not-important-bucket-2"
}

resource "aws_cloudtrail" "example" {
# ... other configuration ...

advanced_event_selector {
name = "Log all S3 objects events except for two S3 buckets"

field_selector {
field = "eventCategory"
equals = ["Data"]
}

field_selector {
field = "resources.ARN"

not_starts_with = [
"${data.aws_s3_bucket.not-important-bucket-1.arn}/",
"${data.aws_s3_bucket.not-important-bucket-2.arn}/"
]
}

field_selector {
field = "resources.type"
equals = ["AWS::S3::Object"]
}
}

advanced_event_selector {
name = "Log readOnly and writeOnly management events"

field_selector {
field = "eventCategory"
equals = ["Management"]
}
}
}
Logging Individual S3 Buckets And Specific Event Names By Using Advanced Event Selectors
data "aws_s3_bucket" "important-bucket-1" {
bucket = "important-bucket-1"
}

data "aws_s3_bucket" "important-bucket-2" {
bucket = "important-bucket-2"
}

data "aws_s3_bucket" "important-bucket-3" {
bucket = "important-bucket-3"
}

resource "aws_cloudtrail" "example" {
# ... other configuration ...

advanced_event_selector {
name = "Log PutObject and DeleteObject events for two S3 buckets"

field_selector {
field = "eventCategory"
equals = ["Data"]
}

field_selector {
field = "eventName"

equals = [
"PutObject",
"DeleteObject"
]
}

field_selector {
field = "resources.ARN"

#The trailing slash is intentional; do not exclude it.
starts_with = [
"${data.aws_s3_bucket.important-bucket-1.arn}/",
"${data.aws_s3_bucket.important-bucket-2.arn}/"
]
}

field_selector {
field = "readOnly"
equals = ["false"]
}

field_selector {
field = "resources.type"
equals = ["AWS::S3::Object"]
}
}

advanced_event_selector {
name = "Log Delete* events for one S3 bucket"

field_selector {
field = "eventCategory"
equals = ["Data"]
}

field_selector {
field = "eventName"
starts_with = ["Delete"]
}

field_selector {
field = "resources.ARN"

equals = [
"${data.aws_s3_bucket.important-bucket-3.arn}/important-prefix"
]
}

field_selector {
field = "readOnly"
equals = ["false"]
}

field_selector {
field = "resources.type"
equals = ["AWS::S3::Object"]
}
}
}
Sending Events to CloudWatch Logs
resource "aws_cloudwatch_log_group" "example" {
name = "Example"
}

resource "aws_cloudtrail" "example" {
# ... other configuration ...

cloud_watch_logs_group_arn = "${aws_cloudwatch_log_group.example.arn}:*" # CloudTrail requires the Log Stream wildcard
}

Config

Config Configuration Recorder
resource "aws_config_configuration_recorder" "foo" {
name = "example"
role_arn = aws_iam_role.r.arn
}

data "aws_iam_policy_document" "assume_role" {
statement {
effect = "Allow"

principals {
type = "Service"
identifiers = ["config.amazonaws.com"]
}

actions = ["sts:AssumeRole"]
}
}

resource "aws_iam_role" "r" {
name = "awsconfig-example"
assume_role_policy = data.aws_iam_policy_document.assume_role.json
}
AWS Managed Rules
resource "aws_config_config_rule" "r" {
name = "example"

source {
owner = "AWS"
source_identifier = "S3_BUCKET_VERSIONING_ENABLED"
}

depends_on = [aws_config_configuration_recorder.foo]
}

resource "aws_config_configuration_recorder" "foo" {
name = "example"
role_arn = aws_iam_role.r.arn
}

data "aws_iam_policy_document" "assume_role" {
statement {
effect = "Allow"

principals {
type = "Service"
identifiers = ["config.amazonaws.com"]
}

actions = ["sts:AssumeRole"]
}
}

resource "aws_iam_role" "r" {
name = "my-awsconfig-role"
assume_role_policy = data.aws_iam_policy_document.assume_role.json
}

data "aws_iam_policy_document" "p" {
statement {
effect = "Allow"
actions = ["config:Put*"]
resources = ["*"]
}
}

resource "aws_iam_role_policy" "p" {
name = "my-awsconfig-policy"
role = aws_iam_role.r.id
policy = data.aws_iam_policy_document.p.json
}
Custom Rules
resource "aws_config_configuration_recorder" "example" {
# ... other configuration ...
}

resource "aws_lambda_function" "example" {
# ... other configuration ...
}

resource "aws_lambda_permission" "example" {
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.example.arn
principal = "config.amazonaws.com"
statement_id = "AllowExecutionFromConfig"
}

resource "aws_config_config_rule" "example" {
# ... other configuration ...

source {
owner = "CUSTOM_LAMBDA"
source_identifier = aws_lambda_function.example.arn
}

depends_on = [
aws_config_configuration_recorder.example,
aws_lambda_permission.example,
]
}
Custom Policies
resource "aws_config_config_rule" "example" {
name = "example"

source {
owner = "CUSTOM_POLICY"

source_detail {
message_type = "ConfigurationItemChangeNotification"
}

custom_policy_details {
policy_runtime = "guard-2.x.x"
policy_text = <<EOF
rule tableisactive when
resourceType == "AWS::DynamoDB::Table" {
configuration.tableStatus == ['ACTIVE']
}

rule checkcompliance when
resourceType == "AWS::DynamoDB::Table"
tableisactive {
supplementaryConfiguration.ContinuousBackupsDescription.pointInTimeRecoveryDescription.pointInTimeRecoveryStatus == "ENABLED"
}
EOF
}
}
}